package cn.lg.soar.database.config;

import cn.lg.soar.database.annotations.SoarTransactional;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.Arrays;

/**
 * spring 事务处理
 * @author luguoxiang 469599794@qq.com
 * @Date: 2024/3/1 21:54
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Aspect
@Component
@RequiredArgsConstructor
@ConditionalOnMissingClass("io.seata.tm.api.GlobalTransactionContext")
public class SoarSpringTransactionAspect {

    private final PlatformTransactionManager transactionManager;

    @Around("@annotation(soarTransactional)")
    public Object around(ProceedingJoinPoint point, SoarTransactional soarTransactional) throws Throwable {
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();
        def.setIsolationLevel(TransactionDefinition.ISOLATION_DEFAULT);
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);

        TransactionStatus status = transactionManager.getTransaction(def);
        Object result;

        try {
            result = point.proceed();
            if (status.isNewTransaction()) {
                transactionManager.commit(status);
            }
        } catch (Throwable ex) {
            if (status.isNewTransaction()) {
                Class<? extends Throwable>[] rollbackFor = soarTransactional.rollbackFor();
                if (rollbackFor.length == 0 || Arrays.stream(rollbackFor).anyMatch(e -> e.isInstance(ex))) {
                    transactionManager.rollback(status);
                }
            }
            throw ex;
        }

        return result;
    }
}